﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;

namespace P3SS.FailedMail
{
	/// <summary>
	/// Manages the list of failed mail items
	/// </summary>
	public class FailedMailEngine
	{
		#region Constants
		/// <summary>
		/// Absolute location of the settings file
		/// </summary>
		private static readonly string FAILED_MAIL_FILE = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\P3SS\\failed_mail.xml";
		#endregion
		#region Constructor
		/// <summary>
		/// Default Constructor
		/// </summary>
		public FailedMailEngine()
		{
			MailItems = new List<FailedMailItem>();
		}
		#endregion
		#region Properties
		/// <summary>
		/// The actual list of failed mail items
		/// </summary>
		public List<FailedMailItem> MailItems { get; set; }
		#endregion
		#region Methods
		/// <summary>
		/// Thread safe wrapper for the add method
		/// </summary>
		/// <param name="inItem">Single item to add</param>
		public void AddItem(FailedMailItem inItem)
		{
			lock (MailItems)
			{
				MailItems.Add(inItem);
			}
		}

		/// <summary>
		/// Thread safe wrapper for the AddRange method
		/// </summary>
		/// <param name="inItems">List of items to add</param>
		public void AddItems(List<FailedMailItem> inItems)
		{
			lock (MailItems)
			{
				MailItems.AddRange(inItems);
			}
		}

		/// <summary>
		/// Removes and returns all items in the list. Thread safe
		/// </summary>
		/// <returns>All items in MailItems</returns>
		public List<FailedMailItem> PopAllItems()
		{
			List<FailedMailItem> outItems;

			lock (MailItems)
			{
				outItems = new List<FailedMailItem>(MailItems);
				MailItems.Clear();
			}

			return outItems;
		}
		#endregion
		#region Static Methods
		/// <summary>
		/// Loads failed mail from file
		/// </summary>
		/// <returns>Loaded failed mail items if successfully read, otherwise a new FailedMailEngine object</returns>
		public static FailedMailEngine LoadFailedMail()
		{
			FailedMailEngine newFailedMail = null;
			FileStream failedMailFile = null;

			try
			{
				XmlSerializer settingsSerializer = new XmlSerializer(typeof(FailedMailEngine));

				failedMailFile = new FileStream(FAILED_MAIL_FILE, FileMode.Open, FileAccess.Read);
				newFailedMail = (FailedMailEngine)settingsSerializer.Deserialize(failedMailFile);
			}
			catch
			{
				newFailedMail = new FailedMailEngine();
				try
				{
					failedMailFile.Close();
				}
				catch { }

				FileInfo settingsFileInfo = new FileInfo(FAILED_MAIL_FILE);
				if (settingsFileInfo.Exists)
					settingsFileInfo.Delete();
			}
			finally
			{
				if (failedMailFile != null)
				{
					failedMailFile.Close();
					failedMailFile.Dispose();
				}
			}

			foreach (FailedMailItem thisItem in newFailedMail.MailItems)
			{
				thisItem.MailItem = thisItem.MailItem.Replace("\n", "\r\n");
			}

			return newFailedMail;
		}

		/// <summary>
		/// Saves failed mail to file
		/// </summary>
		/// <param name="inFailedMail">Failed mail to save</param>
		public static void SaveFailedMail(FailedMailEngine inFailedMail)
		{
			XmlSerializer settingsSerializer = new XmlSerializer(typeof(FailedMailEngine));

			try
			{
				FileInfo failedMailFileInfo = new FileInfo(FAILED_MAIL_FILE);
				CreateFolder(failedMailFileInfo.Directory);
				if (failedMailFileInfo.Exists)
					failedMailFileInfo.Delete();

				FileStream failedMailFile = new FileStream(FAILED_MAIL_FILE, FileMode.Create, FileAccess.Write);
				settingsSerializer.Serialize(failedMailFile, inFailedMail);
				failedMailFile.Flush();
				failedMailFile.Close();
				failedMailFile.Dispose();
			}
			catch
			{
			}
		}

		/// <summary>
		/// Creates a folder and any parents along its path that don't exist
		/// </summary>
		/// <param name="inDirectory">Directory to create</param>
		private static void CreateFolder(DirectoryInfo inDirectory)
		{
			if (!inDirectory.Exists)
			{
				CreateFolder(inDirectory.Parent);
				inDirectory.Create();
			}
		}
		#endregion
	}
}
